package moltencore.network.protocol;

import moltencore.game.Game;
import spark.network.Util;
import spark.network.protocol.Base;

public class CreateGame extends Base{
	public static final int GAME_NAME_LENGTH_MAX = 32;

	public static final short TYPE = 0x13;
	private static final byte[] TYPE_BYTES = Util.makeBytes(TYPE);

	private Game.Mode gameMode;
	private static final int GAME_MODE_OFFSET = Base.SIZE;
	private static final int GAME_MODE_LENGTH = Byte.SIZE / 8;

	private byte playerLimit;
	private static final int PLAYER_LIMIT_OFFSET = GAME_MODE_OFFSET + GAME_MODE_LENGTH;
	private static final int PLAYER_LIMIT_LENGTH = Byte.SIZE / 8;

	/**
	 * When criticalMass bubbles of corresponding color are linked they fall down
	 */
	private byte criticalMass;
	private static final int CRITICAL_MASS_OFFSET = PLAYER_LIMIT_OFFSET + PLAYER_LIMIT_LENGTH;
	private static final int CRITICAL_MASS_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we dont want gravity
	 * !=0  if we want gravity
	 */
	private byte gravity;
	private static final int GRAVITY_OFFSET = CRITICAL_MASS_OFFSET + CRITICAL_MASS_LENGTH;
	private static final int GRAVITY_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we dont want wind
	 * !=0  if we want wind
	 */
	private byte wind;
	private static final int WIND_OFFSET = GRAVITY_OFFSET + GRAVITY_LENGTH;
	private static final int WIND_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we dont want chain reactions
	 * !=0  if we want chain reactions
	 */
	private byte chainReactions;
	private static final int CHAIN_REACTIONS_OFFSET = WIND_OFFSET + WIND_LENGTH;
	private static final int CHAIN_REACTIONS_LENGTH = Byte.SIZE / 8;

	private byte ordinaryBubbleCount;
	private static final int ORFINARY_BUBBLE_COUNT_OFFSET = CHAIN_REACTIONS_OFFSET + CHAIN_REACTIONS_LENGTH;
	private static final int ORFINARY_BUBBLE_COUNT_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we dont want rainbow bubbles
	 * !=0  if we want rainbow bubbles
	 */
	private byte rainbowBubbles;
	private static final int RAINBOW_BUBBLES_OFFSET = ORFINARY_BUBBLE_COUNT_OFFSET + ORFINARY_BUBBLE_COUNT_LENGTH;
	private static final int RAINBOW_BUBBLES_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we dont want blazing bubbles
	 * !=0  if we want blazing bubbles
	 */
	private byte blazingBubbles;
	private static final int BLAZING_BUBBLES_OFFSET = RAINBOW_BUBBLES_OFFSET + RAINBOW_BUBBLES_LENGTH;
	private static final int BLAZING_BUBBLES_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we dont want ghost bubbles
	 * !=0  if we want ghost bubbles
	 */
	private byte ghostBubbles;
	private static final int GHOST_BUBBLES_OFFSET = BLAZING_BUBBLES_OFFSET + BLAZING_BUBBLES_LENGTH;
	private static final int GHOST_BUBBLES_LENGTH = Byte.SIZE / 8;

	/**
	 * 0    if we don't want teams
	 * !=0  if we want teams
	 */
	private byte teams;
	private static final int TEAMS_OFFSET = BLAZING_BUBBLES_OFFSET + BLAZING_BUBBLES_LENGTH;
	private static final int TEAMS_LENGTH = Byte.SIZE / 8;


	/**
	 * Length of the game name
	 */
	private byte gameNameLength;
	private static final int GAME_NAME_LENGTH_OFFSET = TEAMS_OFFSET + TEAMS_LENGTH;
	private static final int GAME_NAME_LENGTH_LENGTH = Byte.SIZE / 8;

	/**
	 * Reason for rejection or something else we want to tell the client.
	 */
	private byte[] gameName;
	private static final int GAME_NAME_OFFSET = GAME_NAME_LENGTH_OFFSET + GAME_NAME_LENGTH_LENGTH;

	private static final int SIZE =
			Base.SIZE +
			GAME_MODE_LENGTH +
			PLAYER_LIMIT_LENGTH +
			CRITICAL_MASS_LENGTH +
			GRAVITY_LENGTH +
			WIND_LENGTH +
			CHAIN_REACTIONS_LENGTH +
			ORFINARY_BUBBLE_COUNT_LENGTH +
			RAINBOW_BUBBLES_LENGTH +
			BLAZING_BUBBLES_LENGTH +
			GHOST_BUBBLES_LENGTH +
			TEAMS_LENGTH +
			GAME_NAME_LENGTH_LENGTH;

	public CreateGame() {
		super(true, TYPE_BYTES);
	}

	public Game.Mode getGameMode() {
		return gameMode;
	}

	public void setGameMode(Game.Mode gameMode) {
		this.gameMode = gameMode;
	}

	public byte getPlayerLimit() {
		return playerLimit;
	}

	public byte getCriticalMass() {
		return criticalMass;
	}

	public void setCriticalMass(byte criticalMass) {
		this.criticalMass = criticalMass;
	}

	public void setPlayerLimit(byte playerLimit) {
		this.playerLimit = playerLimit;
	}

	public boolean getGravity() {
		return gravity != 0;
	}

	public void setGravity(boolean gravity) {
		this.gravity = (byte) (gravity ? 1 : 0);
	}

	public boolean getWind() {
		return wind != 0;
	}

	public void setWind(boolean wind) {
		this.wind = (byte) (wind ? 1 : 0);
	}

	public boolean getChainReactions() {
		return chainReactions != 0;
	}

	public void setChainReactions(boolean chainReactions) {
		this.chainReactions = (byte) (chainReactions ? 1 : 0);
	}

	public byte getOrdinaryBubbleCount() {
		return ordinaryBubbleCount;
	}

	public void setOrdinaryBubbleCount(byte ordinaryBubbleCount) {
		this.ordinaryBubbleCount = ordinaryBubbleCount;
	}

	public boolean getRainbowBubbles() {
		return rainbowBubbles != 0;
	}

	public void setRainbowBubbles(boolean rainbowBubbles) {
		this.rainbowBubbles = (byte) (rainbowBubbles ? 1 : 0);
	}

	public boolean getBlazingBubbles() {
		return blazingBubbles != 0;
	}

	public void setBlazingBubbles(boolean blazingBubbles) {
		this.blazingBubbles = (byte) (blazingBubbles ? 1 : 0);
	}

	public boolean getGhostBubbles() {
		return ghostBubbles != 0;
	}

	public void setGhostBubbles(boolean ghostBubbles) {
		this.ghostBubbles = (byte) (ghostBubbles ? 1 : 0);
	}

	public boolean getTeams() {
		return teams != 0;
	}

	public void setTeams(boolean teams) {
		this.teams = (byte) (teams ? 1 : 0);
	}

	public byte[] getGameName() {
		return gameName;
	}

	public void setGameName(String name) {
		byte[] nameBytes = name.getBytes();

		gameNameLength = (byte) ((nameBytes.length > GAME_NAME_LENGTH_MAX) ? GAME_NAME_LENGTH_MAX : nameBytes.length);

		this.gameName = new byte[gameNameLength];
		System.arraycopy(nameBytes, 0, this.gameName, 0, gameNameLength);
	}

	@Override
	public void getBytes(byte[] buffer) {
		super.getBytes(buffer);
		buffer[GAME_MODE_OFFSET] = gameMode.getType();
		buffer[PLAYER_LIMIT_OFFSET] = playerLimit;
		buffer[CRITICAL_MASS_OFFSET] = criticalMass;
		buffer[GRAVITY_OFFSET] = gravity;
		buffer[WIND_OFFSET] = wind;
		buffer[ORFINARY_BUBBLE_COUNT_OFFSET] = ordinaryBubbleCount;
		buffer[CHAIN_REACTIONS_OFFSET] = chainReactions;
		buffer[RAINBOW_BUBBLES_OFFSET] = rainbowBubbles;
		buffer[BLAZING_BUBBLES_OFFSET] = blazingBubbles;
		buffer[GHOST_BUBBLES_OFFSET] = ghostBubbles;
		buffer[TEAMS_OFFSET] = teams;
		buffer[GAME_NAME_LENGTH_OFFSET] = gameNameLength;
		if(gameName != null) {
			Util.arraycopy(gameName, 0, buffer, GAME_NAME_OFFSET, gameNameLength);
		}
	}

	@Override
	public void fill(byte[] buffer) {
		super.fill(buffer);
		gameMode = Game.Mode.fromType(buffer[GAME_MODE_OFFSET]);
		playerLimit = buffer[PLAYER_LIMIT_OFFSET];
		criticalMass = buffer[CRITICAL_MASS_OFFSET];
		gravity = buffer[GRAVITY_OFFSET];
		wind = buffer[WIND_OFFSET];
		chainReactions = buffer[CHAIN_REACTIONS_OFFSET];
		ordinaryBubbleCount = buffer[ORFINARY_BUBBLE_COUNT_OFFSET];
		rainbowBubbles = buffer[RAINBOW_BUBBLES_OFFSET];
		blazingBubbles = buffer[BLAZING_BUBBLES_OFFSET];
		ghostBubbles = buffer[GHOST_BUBBLES_OFFSET];
		teams = buffer[TEAMS_OFFSET];
		gameNameLength = buffer[GAME_NAME_LENGTH_OFFSET];
		gameName = new byte[gameNameLength];
		Util.arraycopy(buffer, GAME_NAME_OFFSET, gameName, 0, gameNameLength);
	}

	@Override
	public int getSize() {
		return SIZE + gameNameLength;
	}

	public static CreateGame create(byte[] buffer, long receiveTime) {
		CreateGame packet = new CreateGame();
		packet.fill(buffer);
		packet.setTimeStampReceive(receiveTime);
		return packet;
	}
}
